package com.hcj.springcloud.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * java8的新特性，做高并发网络请求，很管用
 * 提升第三方的API调用的效率
 */
@Service
@Slf4j
public class CompletableFutureService {
    @Autowired
    private RestTemplate restTemplate;

    private String getGitHubUserInfoByUserName(String uesrName) {
        log.info("looking up {}", uesrName);
        String url = String.format("https://api.github.com/users/%s", uesrName);
        String forObject = restTemplate.getForObject(url, String.class);
        try {
            int i = new Random().nextInt(10);
            log.info("休眠：{} 秒", i);
            TimeUnit.SECONDS.sleep(i);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
        log.info("result--->:{}", forObject);
        return forObject;
    }

    /**
     * runAsync
     * 如果你想异步的运行一个后台任务并且不想改任务返回任务东西，这时候可以使用
     * CompletableFuture.runAsync()方法，
     * 它持有一个Runnable 对象，
     * 并返回 CompletableFuture<Void>。
     *
     * @param userName
     * @return
     * @throws InterruptedException
     */
    public CompletableFuture<Void> runAsync(String userName, Executor taskExecutor) {
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                getGitHubUserInfoByUserName(userName);
            }
        }, taskExecutor);
        return voidCompletableFuture;
    }


    /**
     * 使用 supplyAsync() 运行一个异步任务并且返回结果
     * 当任务不需要返回任何东西的时候， CompletableFuture.runAsync() 非常有用。但是如果你的后台任务需要返回一些结果应该要怎么样？
     * CompletableFuture.supplyAsync() 就是你的选择。
     * 它持有supplier<T> 并且返回CompletableFuture<T>，T 是通过调用 传入的supplier取得的值的类型。
     *
     * @param userName
     * @return
     */
    public CompletableFuture<String> supplyAsync(String userName, Executor taskExecutor) {
        // lamba的写法
        // CompletableFuture.supplyAsync(() -> {
        //    try {
        //        TimeUnit.SECONDS.sleep(1);
        //    } catch (InterruptedException e) {
        //        throw new IllegalStateException(e);
        //    }
        //    return "Result of the asynchronous computation";
        //});
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return getGitHubUserInfoByUserName(userName);
            }
        }, taskExecutor);
        return stringCompletableFuture;
    }

    /**
     * runAsync
     * 如果你想异步的运行一个后台任务并且不想改任务返回任务东西，这时候可以使用
     * CompletableFuture.runAsync()方法，
     * 它持有一个Runnable 对象，
     * 并返回 CompletableFuture<Void>。
     *
     * @param userName
     * @return
     * @throws InterruptedException
     */
    @Async("taskExecutor")
    public CompletableFuture<Void> runAsync(String userName) {
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                getGitHubUserInfoByUserName(userName);
            }
        });
        return voidCompletableFuture;
    }

    /**
     * 使用 supplyAsync() 运行一个异步任务并且返回结果
     * 当任务不需要返回任何东西的时候， CompletableFuture.runAsync() 非常有用。但是如果你的后台任务需要返回一些结果应该要怎么样？
     * CompletableFuture.supplyAsync() 就是你的选择。
     * 它持有supplier<T> 并且返回CompletableFuture<T>，T 是通过调用 传入的supplier取得的值的类型。
     *
     * @param userName
     * @return
     */
    @Async("taskExecutor")
    public CompletableFuture<String> supplyAsync(String userName) {
        // lamba的写法
        // CompletableFuture.supplyAsync(() -> {
        //    try {
        //        TimeUnit.SECONDS.sleep(1);
        //    } catch (InterruptedException e) {
        //        throw new IllegalStateException(e);
        //    }
        //    return "Result of the asynchronous computation";
        //});
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return getGitHubUserInfoByUserName(userName);
            }
        });
        return stringCompletableFuture;
    }
}
